package com.spsoft.workflow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.spsoft.core.enums.ResponseEnum;
import com.spsoft.core.exception.BaseException;
import com.spsoft.core.model.Result;
import com.spsoft.core.model.bo.TokenUser;
import com.spsoft.core.utils.CollectionUtils;
import com.spsoft.core.utils.StringUtil;
import com.spsoft.workflow.common.WorkFlowConstants;
import com.spsoft.workflow.common.enums.AssigneeTypeEnums;
import com.spsoft.workflow.common.enums.BizStatusEnums;
import com.spsoft.workflow.common.enums.BusFlowTypeEnums;
import com.spsoft.workflow.entity.WfProcessBusiness;
import com.spsoft.workflow.entity.WfProcessLog;
import com.spsoft.workflow.entity.WfProcessTemplates;
import com.spsoft.workflow.mapper.WfProcessBusinessMapper;
import com.spsoft.workflow.model.dto.bus.*;
import com.spsoft.workflow.model.dto.json.ChildNode;
import com.spsoft.workflow.model.vo.biz.*;
import com.spsoft.workflow.service.ProcessBusinessService;
import com.spsoft.workflow.service.WfProcessLogService;
import com.spsoft.workflow.service.WfProcessTemplatesService;
import com.spsoft.workflow.utils.BpmnModelUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.util.ExecutionGraphUtil;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.spsoft.workflow.common.enums.BizStatusEnums.*;

/**
 * <p>
 * 流程业务表;--主键序列 SEQ_WF_PROCESS_BUSINESS 服务实现类
 * </p>
 *
 * @author dlchen
 * @since 2023-11-13
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ProcessBusinessServiceImpl extends ServiceImpl<WfProcessBusinessMapper, WfProcessBusiness> implements ProcessBusinessService {

    private final HistoryService historyService;
    private final WfProcessLogService wfProcessLogService ;
    private final WfProcessTemplatesService wfProcessTemplatesService;
    private final TaskService taskService;

    /**
     * 获取下一个节点id
     *
     * @param templateId 模板id
     * @param nodeId     节点id
     * @param isParent   是父级
     * @return {@link ChildNode}
     */
    @Override
    public ChildNode getNextNodeId(Long templateId,String nodeId,boolean isParent) {
        AtomicReference<ChildNode> node = new AtomicReference<>(null);
        NextFlowDto nextFlowDto = new NextFlowDto();
        nextFlowDto.setTemplateId(templateId);
        Result<FlowNextVo> r = this.getNextFlowNode(nextFlowDto);
        if(ResponseEnum.SUCCESS.getCode().equals(r.getCode())){
            FlowNextVo flowNextVo = r.getData();
            List<ChildNode> userTasknodeLIst = flowNextVo.getUserTasknodeLIst();
            if(!CollUtil.isEmpty(userTasknodeLIst)){
                userTasknodeLIst.forEach(childNode -> {
                    if(isParent && nodeId.equals(childNode.getParentId())){
                        node.set(childNode);
                    }
                    if(!isParent && nodeId.equals(childNode.getId())){
                        node.set(childNode);
                    }
                });
            }

        }
        return node.get();
    }


    /**
     * 已批准
     *
     * @param wfpb           wfpb
     * @param nextHandleUser 下一个句柄用户
     * @param user           使用者
     * @param task           任务
     */
    @Override
    public void approved(WfProcessBusiness wfpb,String nextHandleUser , TokenUser user,Task task) {
        String bizStatus = BUSINESS_STATUS_1.getStatus();

        if (StrUtil.isBlank(task.getAssignee())) {
            taskService.claim(task.getId(), user.getUserCode());
        }
        String taskDefinitionKey = task.getTaskDefinitionKey();
        ChildNode node=getNextNodeId(wfpb.getProcessTemplateId(),taskDefinitionKey,true);
        String roleName = null;
        if(node !=null && AssigneeTypeEnums.ROLE.getTypeName().equals(node.getProps().getAssignedType())
                && StringUtil.isNotEmpty(nextHandleUser)) {
            //获取到下一个环节的角色信息
            List<Map<String, Object>> roles = node.getProps().getRole();
            if(CollUtil.isNotEmpty(roles)){
                for (Map<String, Object> role : roles) {
                    roleName = Convert.toStr(role.get("roleName"));
                    roleName+=",";
                }
                roleName.substring(0,roleName.length()-1);
            }
            //更新业务状态  此处要在执行任务之前去更改，
            updateBizStatus(task.getProcessInstanceId(), bizStatus,roleName);
            //设置下一个环节审核人员
            Map<String, Object> processVariables = new HashMap<>();
            List<String> arrrovalUserList = Lists.newArrayList();
            arrrovalUserList.add(nextHandleUser);
            processVariables.put(node.getId()+"assigneeList",arrrovalUserList);
            taskService.complete(task.getId(),processVariables);
        }else{//其它情况，下一个环节角色， 发起人的角色
            List<WfProcessLog> list = wfProcessLogService.list(new LambdaQueryWrapper<WfProcessLog>()
                    .eq(WfProcessLog::getProcInstId, wfpb.getProcInstId())
                    .eq(WfProcessLog::getNodeId, "root")
            );
            roleName = list.get(0).getRolename();
            //更新业务状态  此处要在执行任务之前去更改，
            updateBizStatus(task.getProcessInstanceId(), bizStatus,roleName);
            taskService.complete(task.getId());
        }

    }

    @Override
    public WfProcessBusiness getProcessBusinessByBusId(Long busId) {
        List<WfProcessBusiness> busById = this.baseMapper.getBusById(busId);
        if(!CollectionUtils.isAnyEmpty(busById)){
            return busById.get(0);
        }
        return null;
    }

    /**
     * 是流程未结束
     *
     * @param deptCode         dept代码
     * @param busId            审批流id
     * @param busFlowTypeEnums 总线流类型枚举
     * @return boolean
     */
    @Override
    public boolean isProcessNotEnd(String deptCode, Long busId, BusFlowTypeEnums busFlowTypeEnums) {
        LambdaQueryWrapper<WfProcessBusiness> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WfProcessBusiness::getDeptCode,deptCode);
        queryWrapper.eq(WfProcessBusiness::getBusType,busFlowTypeEnums.getCode());
        queryWrapper.in(WfProcessBusiness::getBusStatus, BUSINESS_STATUS_1.getStatus(), BUSINESS_STATUS_2.getStatus(), BUSINESS_STATUS_3.getStatus());
        queryWrapper.ne(WfProcessBusiness::getId, busId);
        List<WfProcessBusiness> list = this.list(queryWrapper);
        return !CollectionUtils.isAnyEmpty(list);
    }

    @Override
    public void updateBizStatus(String processInstanceId, String bizStatus,String roleName) {
        LambdaUpdateWrapper<WfProcessBusiness> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(WfProcessBusiness::getBusStatus, bizStatus);
        updateWrapper.set(roleName != null,WfProcessBusiness::getRoleNames,roleName);
        updateWrapper.eq(WfProcessBusiness::getProcInstId, processInstanceId);
        this.update(updateWrapper);
    }
    /**
     * 所有可退回的节点
     *
     * @param taskId
     * @return
     */
    @Override
    public List<TaskVO> findReturnTaskList(String taskId) {
        // 获取传入的当前任务id
        // String taskId = handleProcessDTO.getTaskId();
        // 初始化返回结果列表
        List<TaskVO> userTaskList = new ArrayList<>();
        if (StrUtil.isBlank(taskId)) {
            return userTaskList;
        }
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        if (task == null) {
            return userTaskList;
        }
        // 任务定义key 等于 当前任务节点id
        String taskDefinitionKey = task.getTaskDefinitionKey();
        Process mainProcess = BpmnModelUtils.getMainProcess(task.getProcessDefinitionId());
        // 当前节点
        FlowNode currentFlowElement = (FlowNode) mainProcess.getFlowElement(taskDefinitionKey, true);
        // 查询历史节点实例
        List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).activityType(BpmnXMLConstants.ELEMENT_TASK_USER)
                .finished()
                .orderByHistoricActivityInstanceId().desc().list();
        List<String> activityIdList = activityInstanceList.stream()
                .map(HistoricActivityInstance::getActivityId)
                .filter(activityId -> !taskDefinitionKey.equals(activityId))
                .distinct()
                .collect(Collectors.toList());
        //并行网关内所有节点
        Map<String, ChildNode> parallelMap = BpmnModelUtils.getParallelMap(task.getProcessDefinitionId());
//        Map<String, ChildNode> parallelMap = BpmnModelUtils.getParalleGateWayFirstUserTasklMap(task.getProcessDefinitionId());
        for (String activityId : activityIdList) {
            // 回退到主流程的节点
            FlowNode toBackFlowElement = (FlowNode) mainProcess.getFlowElement(activityId, true);
            // 判断 【工具类判断是否可以从源节点 到 目标节点】
            Set<String> set = new HashSet<>();
            if (toBackFlowElement != null && ExecutionGraphUtil.isReachable(mainProcess, toBackFlowElement, currentFlowElement, set) && !parallelMap.containsKey(activityId)) {
                TaskVO userTask = new TaskVO();
                userTask.setId(activityId);
                userTask.setName(toBackFlowElement.getName());
                userTaskList.add(userTask);
            }
        }
        if (CollUtil.isEmpty(userTaskList)) {
            throw new BaseException("没有可退回的审批节点");
        }
        return userTaskList;
    }

    @Override
    public ChildNode getFlowNodeConfig(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String taskDefinitionKey = task.getTaskDefinitionKey();
        ChildNode flowJson = BpmnModelUtils.getFlowJson(task.getProcessDefinitionId());
        ChildNode childNode = BpmnModelUtils.getChildNode(flowJson, taskDefinitionKey);
        childNode.setBranchs(null);
        childNode.setChildren(null);
        Map<String, Object> refuse = childNode.getProps().getRefuse();
        String type = Convert.toStr(refuse.get("type"));
        String target = Convert.toStr(refuse.get("target"));
        if (StrUtil.isNotBlank(target) && "TO_NODE".equals(type)) {
            refuse.put("targetName", BpmnModelUtils.getChildNode(flowJson, target).getName());
        }
        return childNode;
    }

    /**
     * 获取最后一个可退回的节点
     *
     * @param taskId 当前任务id
     * @return 最后一个可退回的节点
     */
    @Override
    public String getLastNodeCanBack(String taskId) {
        List<TaskVO> userTaskList = findReturnTaskList(taskId);
        if (CollUtil.isEmpty(userTaskList)) {
            throw new BaseException("没有可退回的审批节点");
        }
        return userTaskList.get(0).getId();
    }

    /**
     * 获取下一个流节点
     *
     * @param nextFlowDto
     * @return {@link Result}
     */
    @Override
    public Result<FlowNextVo> getNextFlowNode(NextFlowDto nextFlowDto) {
        FlowNextVo flowNextVo = new FlowNextVo();
        //用户任务节点
        List<ChildNode> userTasknodeLIst = new ArrayList<>();
        //抄送节点
        List<ChildNode> ccTasknodeLIst = new ArrayList<>();
        ChildNode cn = new ChildNode();
        cn.setId("root");
        cn.setName("发起人");
        userTasknodeLIst.add(cn);
        Map<String, Object> processVariables = new HashMap<>();
        String businessKey=nextFlowDto.getId();
        Long templateId=nextFlowDto.getTemplateId();
        String processInstanceId = nextFlowDto.getProcessInstanceId();
        if (StrUtil.isNotBlank(businessKey)) {
            WfProcessBusiness pb = this.getById(businessKey);
            if (pb !=null) {
                templateId = pb.getProcessTemplateId();
            }
        }
        BpmnModel bpmnModel = wfProcessTemplatesService.getBpmnModelByTemplateId(templateId);
        BpmnModelUtils.getNextUserTaskNode(userTasknodeLIst, ccTasknodeLIst, bpmnModel, processVariables, WorkFlowConstants.ROOT_EVENT_ID);
        Map<String, ChildNode> parallelMap = BpmnModelUtils.getParallelMap(bpmnModel);
        List<Task> list=new ArrayList<>();
        if (StrUtil.isNotBlank(businessKey) && StrUtil.isNotBlank(processInstanceId)) {
            list = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        }
        List<String> ruTaskIds = list.stream().map(TaskInfo::getTaskDefinitionKey).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(userTasknodeLIst)) {
            for (ChildNode childNode : userTasknodeLIst) {
                if (parallelMap.containsKey(childNode.getId())) {
                    childNode.setParallelNode(true);
                }
                if (ruTaskIds.contains(childNode.getId())) {
                    childNode.setRunning(true);
                }
                childNode.setBranchs(null);
                childNode.setChildren(null);
            }
        }
        if (CollUtil.isNotEmpty(ccTasknodeLIst)) {
            for (ChildNode childNode : ccTasknodeLIst) {
                childNode.setBranchs(null);
                childNode.setChildren(null);
            }
        }
        flowNextVo.setUserTasknodeLIst(userTasknodeLIst);
        flowNextVo.setCcTasknodeLIst(ccTasknodeLIst);
        return Result.OK(flowNextVo);
    }

    /**
     * 查询我的代办
     *
     * @param pqd 处理查询dto
     * @return {@link Result}
     */
    @Override
    public Result<IPage<BusinessFlowVo>> queryProcessToDo(QueryProcessDto pqd) {
        Page<BusinessFlowVo> page = new Page<>(pqd.getPageNo(), pqd.getPageSize());
        IPage<BusinessFlowVo> wpbp = this.baseMapper.queryProcessToDo(page,pqd).convert(pb -> {
            pb.setBusTypeName(BusFlowTypeEnums.getTypeDesc(pb.getBusType()));
            if(BizStatusEnums.BUSINESS_STATUS_1.getStatus().equals(pb.getBusStatus())){
                pb.setBusinessStatusText("待"+pb.getCurrentActivityName());
            }else{
                pb.setBusinessStatusText(BizStatusEnums.getDescription(pb.getBusStatus()));
            }

            return pb;
        });


        return Result.OK(wpbp);
    }

    @Override
    public Result<BusinessFlowVo> queryProcessToDoBuyId(Long busId, String userCode, String deptCode) {
        List<BusinessFlowVo> businessFlowVos = this.baseMapper.queryProcessToDoBuyId(busId, userCode, deptCode);
        if(businessFlowVos !=null && businessFlowVos.size()>0){
            return Result.OK(businessFlowVos.get(0));
        }
        return Result.OK(new BusinessFlowVo());
    }


    /**
     * 查询过程已完成
     *
     * @param   pqd 处理查询dto
     * @return {@link Result}
     */
    @Override
    public Result<IPage<BusinessFlowVo>> queryProcessHasDone(QueryProcessDto pqd){
        Page<BusinessFlowVo> page = new Page<>(pqd.getPageNo(), pqd.getPageSize());
        IPage<BusinessFlowVo> businessFlowVoIPage = this.baseMapper.queryProcessHasDone(page,pqd).convert(pb -> {
            pb.setBusTypeName(BusFlowTypeEnums.getTypeDesc(pb.getBusType()));
            if(BizStatusEnums.BUSINESS_STATUS_1.getStatus().equals(pb.getBusStatus())){
                pb.setBusinessStatusText("待"+pb.getCurrentActivityName());
            }else{
                pb.setBusinessStatusText(BizStatusEnums.getDescription(pb.getBusStatus()));
            }
            return pb;
        });
        return Result.OK(businessFlowVoIPage) ;
    }

    /**
     * 查询自己启动进程
     *
     * @param pqd 处理查询dto
     * @return {@link Result}
     */
    @Override
    public Result<IPage<BusinessFlowVo>> queryStartProcessMyself(QueryProcessDto pqd) {
        Page<BusinessFlowVo> page = new Page<>(pqd.getPageNo(), pqd.getPageSize());
        IPage<BusinessFlowVo> wpbp = this.baseMapper.queryStartProcessMyself(page,pqd).convert(pb -> {
            pb.setBusTypeName(BusFlowTypeEnums.getTypeDesc(pb.getBusType()));
            if(BizStatusEnums.BUSINESS_STATUS_1.getStatus().equals(pb.getBusStatus())){
                pb.setBusinessStatusText("待"+pb.getCurrentActivityName());
            }else{
                pb.setBusinessStatusText(BizStatusEnums.getDescription(pb.getBusStatus()));
            }
            return pb;
        });
        return Result.OK(wpbp);
    }


    /**
     * 查找当前节点下一个节点的审批用户
     *
     * @param f f
     * @return {@link Result}
     */
    @Override
    public Result findApprovalUser(FindApprovalUserDto f){
        String busId = f.getBusId();
        String processInstanceId = f.getProcessInstanceId();
        String nodeId =f.getNodeId();
        WfProcessBusiness wfp = this.getById(busId);
        if(wfp == null){
            return Result.ERROR("业务流程不存在");
        }
        List<BaseUserVo> list =Lists.newArrayList();
        //获取到流程节点配置
        ChildNode node=getNextNodeId(wfp.getProcessTemplateId(),nodeId,true);
        if(node !=null && AssigneeTypeEnums.ROLE.getTypeName().equals(node.getProps().getAssignedType())) {
            List<Map<String, Object>> roles = node.getProps().getRole();
            if(CollUtil.isEmpty(roles)){
                return Result.ERROR("审批节点未配置角色");
            }
            //取出roleid 转换集合
            List<String> roleIds = roles.stream().map(role -> Convert.toStr(role.get("id"))).collect(Collectors.toList());
            list= this.baseMapper.findApprovalUser(wfp.getDeptCode(),roleIds);
        }else{
            BaseUserVo baseUser = wfProcessLogService.queryRootUser(processInstanceId, busId, "root");
            list.add(baseUser);
        }
        return Result.OK(list);
    }


    /**
     * 新增是获取流程审批人员
     *
     * @param busType  总线类型
     * @param deptCode dept代码
     * @return {@link Result}
     */
    @Override
    public Result newfindApprovalUser(String busType,String deptCode){
        if(StringUtil.isNotEmpty(busType) && StringUtil.isNotEmpty(deptCode)) {
            BusFlowTypeEnums busFlowTypeEnums = BusFlowTypeEnums.valueOf(busType);
            WfProcessTemplates template = wfProcessTemplatesService.getFormTemplateByBusType(busFlowTypeEnums);
            if (template == null) {
                return Result.ERROR("业务流程不存在");
            }
            List<BaseUserVo> list = Lists.newArrayList();
            //获取到流程节点配置
            ChildNode node = getNextNodeId(template.getId(), "root", true);
            if (AssigneeTypeEnums.ROLE.getTypeName().equals(node.getProps().getAssignedType())) {
                List<Map<String, Object>> roles = node.getProps().getRole();
                if (CollUtil.isEmpty(roles)) {
                    return Result.ERROR("审批节点未配置角色");
                }
                //取出roleid 转换集合
                List<String> roleIds = roles.stream().map(role -> Convert.toStr(role.get("id"))).collect(Collectors.toList());
                list = this.baseMapper.findApprovalUser(deptCode, roleIds);
            }
            return Result.OK(list);
        }else{
            return Result.ValidateError("业务类型或机构编码不能为空");
        }
    }

}
